home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / filutil / bison110.zip / GETOPT.C < prev    next >
Text File  |  1990-05-06  |  14KB  |  503 lines

  1. /*
  2. GNU getopt for MSDOS and Microsoft C.
  3.  
  4. Enclosed:
  5.  
  6.     *  README        This file (the appetizer).
  7.     *  GETOPT.C        The main course.
  8.     *  MAKEFILE        The dessert, an ndmake-file
  9.                   that makes libraries and a test
  10.                   program.
  11.  
  12. The MSDOS-specific capabilities are invoked
  13. by defining MSDOS_FLAGS at compile time.
  14. In particular, "/" is allowed as a synonym
  15. for "-" and "/-" as a synonym for "--".
  16.  
  17. I chose to hack the GNU getopt after hacking
  18. the AT&T getopt and realising that it wouldn't
  19. allow for "/" style flags after non-option arguments--
  20. not very MSDOS-like behaviour.
  21.  
  22.     -- Barry Schwartz, Dec. 1989
  23. */
  24.  
  25. /*
  26.  * GNU getopt, hacked for MSDOS and Microsoft C 5.1 and QuickC.
  27.  *
  28.  * Barry Schwartz, Dec. 1989
  29.  *
  30.  * New compile-time defs:
  31.  *   MSDOS            Defined by Microsoft compilers
  32.  *   MSDOS_FLAGS        Allow "/" as a synonym for "-"
  33.  *                and "/-" as a synonym for "--"
  34.  */
  35.  
  36. #define MSDOS_FLAGS
  37.  
  38. /*
  39.  * This version of `getopt' appears to the caller like standard Unix
  40.  * `getopt' but it behaves differently for the user, since it allows the
  41.  * user to intersperse the options with the other arguments.
  42.  *
  43.  * As `getopt' works, it permutes the elements of `argv' so that, when it is
  44.  * done, all the options precede everything else.  Thus all application
  45.  * programs are extended to handle flexible argument order.
  46.  *
  47.  * Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
  48.  * Then the behavior is completely standard.
  49.  *
  50.  * GNU application programs can use a third alternative mode in which they
  51.  * can distinguish the relative order of options and other arguments.
  52.  */
  53.  
  54. #include <stdio.h>
  55. #if defined(MSDOS)
  56. #include <malloc.h>
  57. #include <string.h>
  58. extern char *getenv(char *);
  59. #endif
  60.  
  61. #if defined(sparc)
  62. #include <alloca.h>
  63. #endif
  64. #if defined(USG) || defined(MSDOS)
  65. #define bcopy(s, d, l)    memcpy((d), (s), (l))
  66. #define index        strchr
  67. #endif
  68.  
  69. /*
  70.  * For communication from `getopt' to the caller. When `getopt' finds an
  71.  * option that takes an argument, the argument value is returned here.
  72.  * Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element
  73.  * is returned here.
  74.  */
  75.  
  76. char           *optarg = 0;
  77.  
  78. /*
  79.  * Index in ARGV of the next element to be scanned. This is used for
  80.  * communication to and from the caller and for communication between
  81.  * successive calls to `getopt'.
  82.  *
  83.  * On entry to `getopt', zero means this is the first call; initialize.
  84.  *
  85.  * When `getopt' returns EOF, this is the index of the first of the
  86.  * non-option elements that the caller should itself scan.
  87.  *
  88.  * Otherwise, `optind' communicates from one call to the next how much of
  89.  * ARGV has been scanned so far.
  90.  */
  91.  
  92. int             optind = 0;
  93.  
  94. /*
  95.  * The next char to be scanned in the option-element in which the last
  96.  * option character we returned was found. This allows us to pick up the
  97.  * scan where we left off.
  98.  *
  99.  * If this is zero, or a null string, it means resume the scan by advancing
  100.  * to the next ARGV-element.
  101.  */
  102.  
  103. static char    *nextchar;
  104.  
  105. /*
  106.  * Callers store zero here to inhibit the error message for unrecognized
  107.  * options.
  108.  */
  109.  
  110. int             opterr = 1;
  111.  
  112. /*
  113.  * Describe how to deal with options that follow non-option ARGV-elements.
  114.  *
  115.  * UNSPECIFIED means the caller did not specify anything; the default is then
  116.  * REQUIRE_ORDER if the environment variable _OPTIONS_FIRST is defined,
  117.  * PERMUTE otherwise.
  118.  *
  119.  * REQUIRE_ORDER means don't recognize them as options. Stop option
  120.  * processing when the first non-option is seen. This is what Unix does.
  121.  *
  122.  * PERMUTE is the default.  We permute the contents of `argv' as we scan, so
  123.  * that eventually all the options are at the end.  This allows options to
  124.  * be given in any order, even with programs that were not written to
  125.  * expect this.
  126.  *
  127.  * RETURN_IN_ORDER is an option available to programs that were written to
  128.  * expect options and other ARGV-elements in any order and that care about
  129.  * the ordering of the two.  We describe each non-option ARGV-element as
  130.  * if it were the argument of an option with character code zero. Using
  131.  * `-' as the first character of the list of option characters requests
  132.  * this mode of operation.
  133.  *
  134.  * The special argument `--' forces an end of option-scanning regardless of
  135.  * the value of `ordering'.  In the case of RETURN_IN_ORDER, only `--' can
  136.  * cause `getopt' to return EOF with `optind' != ARGC.
  137.  */
  138.  
  139. static enum
  140. {
  141.     REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
  142. }               ordering;
  143.  
  144.  
  145. /* Handle permutation of arguments.  */
  146.  
  147. /*
  148.  * Describe the part of ARGV that contains non-options that have been
  149.  * skipped.  `first_nonopt' is the index in ARGV of the first of them;
  150.  * `last_nonopt' is the index after the last of them.
  151.  */
  152.  
  153. static int      first_nonopt;
  154. static int      last_nonopt;
  155.  
  156. /*
  157.  * Exchange two adjacent subsequences of ARGV. One subsequence is elements
  158.  * [first_nonopt,last_nonopt) which contains all the non-options that have
  159.  * been skipped so far. The other is elements [last_nonopt,optind), which
  160.  * contains all the options processed since those non-options were
  161.  * skipped.
  162.  *
  163.  * `first_nonopt' and `last_nonopt' are relocated so that they describe the
  164.  * new indices of the non-options in ARGV after they are moved.
  165.  */
  166.  
  167. static void exchange(char **argv)
  168. {
  169.     int             nonopts_size
  170.     = (last_nonopt - first_nonopt) * sizeof(char *);
  171.     char          **temp = (char **) alloca(nonopts_size);
  172.  
  173.     /* Interchange the two blocks of data in argv.  */
  174.  
  175.     bcopy(&argv[first_nonopt], temp, nonopts_size);
  176.     bcopy(&argv[last_nonopt], &argv[first_nonopt],
  177.       (optind - last_nonopt) * sizeof(char *));
  178.     bcopy(temp, &argv[first_nonopt + optind - last_nonopt],
  179.       nonopts_size);
  180.  
  181.     /* Update records for the slots the non-options now occupy.  */
  182.  
  183.     first_nonopt += (optind - last_nonopt);
  184.     last_nonopt = optind;
  185. }
  186.  
  187.  
  188. /*
  189.  * Scan elements of ARGV (whose length is ARGC) for option characters
  190.  * given in OPTSTRING.
  191.  *
  192.  * If an element of ARGV starts with '-', and is not exactly "-" or "--",
  193.  * then it is an option element.  The characters of this element (aside
  194.  * from the initial '-') are option characters.  If `getopt' is called
  195.  * repeatedly, it returns successively each of theoption characters from
  196.  * each of the option elements.
  197.  *
  198.  * If `getopt' finds another option character, it returns that character,
  199.  * updating `optind' and `nextchar' so that the next call to `getopt' can
  200.  * resume the scan with the following option character or ARGV-element.
  201.  *
  202.  * If there are no more option characters, `getopt' returns `EOF'. Then
  203.  * `optind' is the index in ARGV of the first ARGV-element that is not an
  204.  * option.  (The ARGV-elements have been permuted so that those that are
  205.  * not options now come last.)
  206.  *
  207.  * OPTSTRING is a string containing the legitimate option characters. A colon
  208.  * in OPTSTRING means that the previous character is an option that wants
  209.  * an argument.  The argument is taken from the rest of the current
  210.  * ARGV-element, or from the following ARGV-element, and returned in
  211.  * `optarg'.
  212.  *
  213.  * If an option character is seen that is not listed in OPTSTRING, return '?'
  214.  * after printing an error message.  If you set `opterr' to zero, the
  215.  * error message is suppressed but we still return '?'.
  216.  *
  217.  * If a char in OPTSTRING is followed by a colon, that means it wants an arg,
  218.  * so the following text in the same ARGV-element, or the text of the
  219.  * following ARGV-element, is returned in `optarg.  Two colons mean an
  220.  * option that wants an optional arg; if there is text in the current
  221.  * ARGV-element, it is returned in `optarg'.
  222.  *
  223.  * If OPTSTRING starts with `-', it requests a different method of handling
  224.  * the non-option ARGV-elements.  See the comments about RETURN_IN_ORDER,
  225.  * above.
  226.  */
  227.  
  228. int getopt(int argc, char **argv, char *optstring)
  229. {
  230.     /*
  231.      * Initialize the internal data when the first call is made. Start
  232.      * processing options with ARGV-element 1 (since ARGV-element 0 is the
  233.      * program name); the sequence of previously skipped non-option
  234.      * ARGV-elements is empty.
  235.      */
  236.  
  237.     if (optind == 0)
  238.     {
  239.     first_nonopt = last_nonopt = optind = 1;
  240.  
  241.     nextchar = 0;
  242.  
  243.     /*
  244.      * Determine how to handle the ordering of options and nonoptions.
  245.      */
  246.  
  247.     if (optstring[0] == '-')
  248.         ordering = RETURN_IN_ORDER;
  249.     else
  250.     if (getenv("_POSIX_OPTION_ORDER") != 0)
  251.         ordering = REQUIRE_ORDER;
  252.     else
  253.         ordering = PERMUTE;
  254.     }
  255.  
  256.     if (nextchar == 0 || *nextchar == 0)
  257.     {
  258.     if (ordering == PERMUTE)
  259.     {
  260.         /*
  261.          * If we have just processed some options following some
  262.          * non-options, exchange them so that the options come first.
  263.          */
  264.  
  265.         if (first_nonopt != last_nonopt && last_nonopt != optind)
  266.         exchange(argv);
  267.         else
  268.         if (last_nonopt != optind)
  269.         first_nonopt = optind;
  270.  
  271.         /*
  272.          * Now skip any additional non-options and extend the range of
  273.          * non-options previously skipped.
  274.          */
  275.  
  276.         while (optind < argc
  277. #if !defined(MSDOS_FLAGS)
  278.            && (argv[optind][0] != '-'
  279. #else
  280.            && ((argv[optind][0] != '-' && argv[optind][0] != '/')
  281. #endif
  282.                || argv[optind][1] == 0))
  283.         optind++;
  284.         last_nonopt = optind;
  285.     }
  286.  
  287.     /*
  288.      * Special ARGV-element `--' means premature end of options. Skip
  289.      * it like a null option, then exchange with previous non-options
  290.      * as if it were an option, then skip everything else like a
  291.      * non-option.
  292.      */
  293.  
  294. #if !defined(MSDOS_FLAGS)
  295.     if (optind != argc && !strcmp(argv[optind], "--"))
  296. #else
  297.     if (optind != argc &&
  298.         !(strcmp(argv[optind], "--") && strcmp(argv[optind], "/-")))
  299. #endif
  300.     {
  301.         optind++;
  302.  
  303.         if (first_nonopt != last_nonopt && last_nonopt != optind)
  304.         exchange(argv);
  305.         else
  306.         if (first_nonopt == last_nonopt)
  307.         first_nonopt = optind;
  308.         last_nonopt = argc;
  309.  
  310.         optind = argc;
  311.     }
  312.  
  313.     /*
  314.      * If we have done all the ARGV-elements, stop the scan and back
  315.      * over any non-options that we skipped and permuted.
  316.      */
  317.  
  318.     if (optind == argc)
  319.     {
  320.         /*
  321.          * Set the next-arg-index to point at the non-options that we
  322.          * previously skipped, so the caller will digest them.
  323.          */
  324.         if (first_nonopt != last_nonopt)
  325.         optind = first_nonopt;
  326.         return EOF;
  327.     }
  328.  
  329.     /*
  330.      * If we have come to a non-option and did not permute it, either
  331.      * stop the scan or describe it to the caller and pass it by.
  332.      */
  333.  
  334. #if !defined(MSDOS_FLAGS)
  335.     if (argv[optind][0] != '-' || argv[optind][1] == 0)
  336. #else
  337.     if ((argv[optind][0] != '-' && argv[optind][0] != '/')
  338.         || argv[optind][1] == 0)
  339. #endif
  340.     {
  341.         if (ordering == REQUIRE_ORDER)
  342.         return EOF;
  343.         optarg = argv[optind++];
  344.         return 0;
  345.     }
  346.  
  347.     /*
  348.      * We have found another option-ARGV-element. Start decoding its
  349.      * characters.
  350.      */
  351.  
  352.     nextchar = argv[optind] + 1;
  353.     }
  354.  
  355.     /* Look at and handle the next option-character.  */
  356.  
  357.     {
  358.     char            c = *nextchar++;
  359.     char           *temp = (char *) index(optstring, c);
  360.  
  361.     /*
  362.      * Increment `optind' when we start to process its last character.
  363.      */
  364.     if (*nextchar == 0)
  365.         optind++;
  366.  
  367.     if (temp == 0 || c == ':')
  368.     {
  369.         if (opterr != 0)
  370.         {
  371.         if (c < 040 || c >= 0177)
  372.                     fprintf(stderr, "\n%s: unrecognized option, character code 0%o\n",
  373.                 argv[0], c);
  374.         else
  375.                     fprintf(stderr, "\n%s: unrecognized option `-%c'\n",
  376.                 argv[0], c);
  377.         }
  378.         return '?';
  379.     }
  380.     if (temp[1] == ':')
  381.     {
  382.         if (temp[2] == ':')
  383.         {
  384.         /*
  385.          * This is an option that accepts an argument optionally.
  386.          */
  387.         if (*nextchar != 0)
  388.         {
  389.             optarg = nextchar;
  390.             optind++;
  391.         }
  392.         else
  393.             optarg = 0;
  394.         nextchar = 0;
  395.         }
  396.         else
  397.         {
  398.         /*
  399.          * This is an option that requires an argument.
  400.          */
  401.         if (*nextchar != 0)
  402.         {
  403.             optarg = nextchar;
  404.             /*
  405.              * If we end this ARGV-element by taking the rest as
  406.              * an arg, we must advance to the next element now.
  407.              */
  408.             optind++;
  409.         }
  410.         else
  411.         if (optind == argc)
  412.         {
  413.             if (opterr != 0)
  414.                         fprintf(stderr, "\n%s: no argument for `-%c' option\n",
  415.                 argv[0], c);
  416.             c = '?';
  417.         }
  418.         else
  419.             /*
  420.              * We already incremented `optind' once; increment it
  421.              * again when taking next ARGV-elt as argument.
  422.              */
  423.             optarg = argv[optind++];
  424.         nextchar = 0;
  425.         }
  426.     }
  427.     return c;
  428.     }
  429. }
  430.  
  431.  
  432. #if defined(TEST)
  433.  
  434. /*
  435.  * Compile with -DTEST to make an executable for use in testing the above
  436.  * definition of `getopt'.
  437.  */
  438.  
  439. int
  440. main(argc, argv)
  441. int             argc;
  442. char          **argv;
  443. {
  444.     char            c;
  445.     int             digit_optind = 0;
  446.  
  447.     while (1)
  448.     {
  449.     int             this_option_optind = optind;
  450.     if ((c = getopt(argc, argv, "abc:d:0123456789")) == EOF)
  451.         break;
  452.  
  453.     switch (c)
  454.     {
  455.     case '0':
  456.     case '1':
  457.     case '2':
  458.     case '3':
  459.     case '4':
  460.     case '5':
  461.     case '6':
  462.     case '7':
  463.     case '8':
  464.     case '9':
  465.         if (digit_optind != 0 && digit_optind != this_option_optind)
  466.         printf("digits occur in two different argv-elements.\n");
  467.         digit_optind = this_option_optind;
  468.         printf("option %c\n", c);
  469.         break;
  470.  
  471.     case 'a':
  472.         printf("option a\n");
  473.         break;
  474.  
  475.     case 'b':
  476.         printf("option b\n");
  477.         break;
  478.  
  479.     case 'c':
  480.         printf("option c with value `%s'\n", optarg);
  481.         break;
  482.  
  483.     case '?':
  484.         break;
  485.  
  486.     default:
  487.         printf("?? getopt returned character code 0%o ??\n", c);
  488.     }
  489.     }
  490.  
  491.     if (optind < argc)
  492.     {
  493.     printf("non-option ARGV-elements: ");
  494.     while (optind < argc)
  495.         printf("%s ", argv[optind++]);
  496.     printf("\n");
  497.     }
  498.  
  499.     return 0;
  500. }
  501.  
  502. #endif
  503.